#include "ProjectReader.h"
#include "../Program.h"
#include "../entity/EntityLight.h"
#include "../entity/EntityCamera.h"
#include "../entity/EntitySceneNode.h"

/**
 * @brief 读取项目文件
 * @param filename
 * @return
 */
bool ProjectReader::Read(const QString& filename)
{
    QFile file(filename);
    if (!file.open(QFileDevice::ReadOnly | QFile::Text)) return false;
    QDomDocument doc;
    doc.setContent(&file);
    file.close();
    QDomElement project = doc.documentElement();
    if (project.tagName() == "project")
    {
        Program::WorkingSpace = filename;
        readLight(project);
        readCamera(project);
        readSetting(project);
        readScene(project);
        readScript(project);
        readExtra(project);
        return true;
    }
    return false;
}

void ProjectReader::readLight(QDomElement& project)
{
    QDomElement light = project.namedItem("light").toElement();
    Program::LightParam.Parse(light.attribute("type"));
    QDomNode node1 = light.namedItem("position");
    Program::LightParam.Position.Parse(node1.toElement().text());
    QDomNode node2 = light.namedItem("direction");
    Program::LightParam.Direction.Parse(node2.toElement().text());
    QDomNode node3 = light.namedItem("range");
    Program::LightParam.Range = node3.toElement().text().toFloat();
    QDomNode node4 = light.namedItem("angle");
    Program::LightParam.Angle = node4.toElement().text().toFloat();
    QDomNode node5 = light.namedItem("color");
    Program::LightParam.Color.Parse(node5.toElement().text());
    QDomNode node6 = light.namedItem("bind_camera");
    Program::LightParam.BindCamera = node6.toElement().text() == "true";
}

void ProjectReader::readCamera(QDomElement& project)
{
    QDomElement camera = project.namedItem("camera").toElement();
    Program::CameraParam.Parse(camera.attribute("type"));
    QDomNode node1 = camera.namedItem("position");
    Program::CameraParam.Position.Parse(node1.toElement().text());
    QDomNode node2 = camera.namedItem("angle");
    Program::CameraParam.Angle.Parse(node2.toElement().text());
    QDomNode node3 = camera.namedItem("target");
    Program::CameraParam.Target.Parse(node3.toElement().text());
    QDomNode node4 = camera.namedItem("fov");
    Program::CameraParam.FieldOfView = node4.toElement().text().toFloat();
    QDomNode node5 = camera.namedItem("near");
    Program::CameraParam.ClipNear = node5.toElement().text().toFloat();
    QDomNode node6 = camera.namedItem("far");
    Program::CameraParam.ClipFar = node6.toElement().text().toFloat();
}

void ProjectReader::readSetting(QDomElement& project)
{
    QDomElement setting = project.namedItem("setting").toElement();
    QDomNode node1 = setting.namedItem("background");
    Program::Config.Background.Parse(node1.toElement().text());
    QDomNode node2 = setting.namedItem("gravity");
    Program::Config.Gravity.Parse(node2.toElement().text());
    QDomNode node3 = setting.namedItem("input_mouse_map");
    QStringList val1 = node3.toElement().text().split(',');
    Program::Config.InputMouseMap[0] = val1[0].toInt();
    Program::Config.InputMouseMap[1] = val1[1].toInt();
    Program::Config.InputMouseMap[2] = val1[2].toInt();
    QDomNode node4 = setting.namedItem("input_speed");
    QStringList val2 = node4.toElement().text().split(',');
    Program::Config.InputSpeed[0] = val2[0].toFloat();
    Program::Config.InputSpeed[1] = val2[1].toFloat();
    QDomNode node5 = setting.namedItem("input_disable");
    QStringList val3 = node5.toElement().text().split(',');
    Program::Config.InputDisable[0] = val3[0] == "true";
    Program::Config.InputDisable[1] = val3[1] == "true";
    Program::Config.InputDisable[2] = val3[2] == "true";
    QDomNode node6 = setting.namedItem("plugin");
    QString val4 = node6.toElement().text().trimmed();
    if (val4.isEmpty()) Program::Config.PluginUsed.clear();
    else Program::Config.PluginUsed = val4.split(',');
}

void ProjectReader::readScene(QDomElement& project)
{
    QDomElement scene = project.namedItem("scene").toElement();
    Program::SceneNodeList.clear();
    QMap<QString, QString> parentLookup;
    QDomNodeList nodeList = scene.elementsByTagName("node");
    for (int i = 0; i < nodeList.count(); i++)
    {
        QDomElement item = nodeList.at(i).toElement();
        QString name = item.attribute("name");
        QString type = item.attribute("type");
        QString parent = item.attribute("parent");
        QDomNode node1 = item.namedItem("attribute");
        parentLookup.insert(name, parent);
        EntitySceneNode* sceneNode = new EntitySceneNode(nullptr, type, name, node1.toElement().text());
        QDomNode node2 = item.namedItem("position");
        sceneNode->Position.Parse(node2.toElement().text());
        QDomNode node3 = item.namedItem("angle");
        sceneNode->Angle.Parse(node3.toElement().text());
        QDomNode node4 = item.namedItem("scale");
        sceneNode->Scale.Parse(node4.toElement().text());
        QDomNode node5 = item.namedItem("visible");
        sceneNode->Visible = node5.toElement().text() == "true";
        QDomElement node6 = item.namedItem("physical").toElement();
        sceneNode->Physical.Enable = node6.attribute("enable") == "true";
        QDomNode subject1 = node6.namedItem("trigger");
        if (!subject1.isNull()) sceneNode->Physical.Trigger = subject1.toElement().text() == "true";
        QDomNode subject2 = node6.namedItem("allow_sleep");
        if (!subject2.isNull()) sceneNode->Physical.AllowSleep = subject2.toElement().text() == "true";
        QDomNode subject3 = node6.namedItem("shape");
        if (!subject3.isNull())
        {
            QString val = subject3.toElement().text().trimmed();
            if (val.isEmpty()) sceneNode->Physical.ShapeList.clear();
            else sceneNode->Physical.ShapeList = val.split(',');
        }
        QDomNode subject4 = node6.namedItem("mass");
        if (!subject4.isNull()) sceneNode->Physical.Mass = subject4.toElement().text().toFloat();
        QDomNode subject5 = node6.namedItem("linear_damping");
        if (!subject5.isNull()) sceneNode->Physical.LinearDamping = subject5.toElement().text().toFloat();
        QDomNode subject6 = node6.namedItem("angular_damping");
        if (!subject6.isNull()) sceneNode->Physical.AngularDamping = subject6.toElement().text().toFloat();
        QDomNode subject7 = node6.namedItem("friction");
        if (!subject7.isNull()) sceneNode->Physical.Friction = subject7.toElement().text().toFloat();
        QDomNode subject8 = node6.namedItem("restitution");
        if (!subject8.isNull()) sceneNode->Physical.Restitution = subject8.toElement().text().toFloat();
        QDomElement node7 = item.namedItem("material").toElement();
        sceneNode->Material.Enable = node7.attribute("enable") == "true";
        sceneNode->Material.IsCullFace = node7.attribute("cull_face", "true") == "true";
        sceneNode->Material.IsAddColor = node7.attribute("add_color", "false") == "true";
        sceneNode->Material.IsUseDepth = node7.attribute("use_depth", "true") == "true";
        sceneNode->Material.IsWireframe = node7.attribute("wireframe", "false") == "true";
        QDomNode subject9 = node7.namedItem("roughness");
        if (!subject9.isNull()) sceneNode->Material.Roughness = subject9.toElement().text().toFloat();
        QDomNode subject10 = node7.namedItem("metalness");
        if (!subject10.isNull()) sceneNode->Material.Metalness = subject10.toElement().text().toFloat();
        QDomNode subject11 = node7.namedItem("color");
        if (!subject11.isNull()) sceneNode->Material.Color = subject11.toElement().text();
        QDomNode subject12 = node7.namedItem("emissive");
        if (!subject12.isNull()) sceneNode->Material.Emissive = subject12.toElement().text().toFloat();
        QDomNode node8 = item.namedItem("script");
        QString script = QString(QByteArray::fromBase64(node8.toElement().text().toUtf8()));
        if (!script.isEmpty()) sceneNode->ExtraScript = script.split('\n');
        Program::SceneNodeList.insert(name, std::shared_ptr<EntitySceneNode>(sceneNode));
    }
    QMapIterator<QString, QString> iter(parentLookup);
    while (iter.hasNext())
    {
        iter.next();
        if (!iter.value().isEmpty())
        {
            Program::SceneNodeList[iter.key()]->Parent = Program::SceneNodeList[iter.value()].get();
        }
    }
}

void ProjectReader::readScript(QDomElement& project)
{
    Program::UserScript.clear();
    QDomElement script = project.namedItem("script").toElement();
    QDomNodeList node1 = script.elementsByTagName("text");
    for (int i = 0; i < node1.length(); i++)
    {
        QDomElement node = node1.at(i).toElement();
        QString name = node.attribute("name");
        QString content = QByteArray::fromBase64(node.text().toUtf8());
        Program::UserScript.append(QPair<QString, QString>(name, content));
    }
}

void ProjectReader::readExtra(QDomElement& project)
{
    QDomElement extra = project.namedItem("extra").toElement();
    QDomNode node1 = extra.namedItem("track");
    Program::LineTrack = node1.toElement().text();
}
